package com.yichen.ycOJ.judge.strategy;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;

import com.yichen.ycOJ.common.ErrorCode;
import com.yichen.ycOJ.exception.BusinessException;
import com.yichen.ycOJ.judge.codesandbox.factory.CodeSandBoxFactory;
import com.yichen.ycOJ.judge.codesandbox.model.ExecutionCodeRequest;
import com.yichen.ycOJ.judge.codesandbox.model.ExecutionCodeResponse;
import com.yichen.ycOJ.judge.codesandbox.proxy.CodeSandBoxProxy;
import com.yichen.ycOJ.model.dto.judge.JudgeContext;
import com.yichen.ycOJ.model.dto.question.JudgeCase;
import com.yichen.ycOJ.model.dto.question.JudgeConfig;
import com.yichen.ycOJ.model.dto.questionSubmit.JudgeInfo;
import com.yichen.ycOJ.model.entity.QuestionSubmit;
import com.yichen.ycOJ.model.enums.JudgeInfoMessageEnum;
import com.yichen.ycOJ.model.enums.QuestionSubmitStatusEnum;
import com.yichen.ycOJ.service.QuestionSubmitService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: yichenwu
 * @Date:2024/6/24 15:19
 */

/**
 *  Java 判题策略
 */
public class JavaJudgeStrategy implements JudgeStrategy{

    @Autowired
    private QuestionSubmitService questionSubmitService;

    private static final Long JAVA_TIME = 4L;

    @Override
    public List<JudgeInfo> doJudge(JudgeContext judgeContext) {
        Integer status = judgeContext.getStatus();
        String type = judgeContext.getType();
        String code = judgeContext.getCode();
        String language = judgeContext.getLanguage();
        String judgeCase = judgeContext.getJudgeCase();
//        JudgeInfo judgeInfo = judgeContext.getJudgeInfo();
        JudgeConfig judgeConfig = judgeContext.getJudgeConfig();
        Long questionSubmitId = judgeContext.getQuestionSubmitId();

        // 4. 进行判题
        // 4.1 检查判题状态是否符合条件
        if (!status.equals(QuestionSubmitStatusEnum.WAITING.getValue())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "正在判题中或者判题失败，请稍等再次提交");
        }

        // 4.2 修改题目提交状态为 判题中
        status = QuestionSubmitStatusEnum.RUNNING.getValue();
        boolean update = questionSubmitService.update(null, new UpdateWrapper<QuestionSubmit>()
                .set("status", status)
                .eq("id",questionSubmitId));
        if (!update) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "修改数据库判题状态信息RUNNING");
        }

        // 4.3 调用代码沙箱执行代码
        CodeSandBoxProxy codeSandBoxProxy = new CodeSandBoxProxy(CodeSandBoxFactory.getCodeSandBox(type));
        ExecutionCodeRequest executionCodeRequest = new ExecutionCodeRequest();
        executionCodeRequest.setCode(code);
        executionCodeRequest.setLanguage(language);
        List<String> input = JSONUtil.toList(judgeCase, JudgeCase.class).stream().map(JudgeCase::getInput).collect(Collectors.toList());
        executionCodeRequest.setInput(input);

        List<ExecutionCodeResponse> executionCodeResponse = codeSandBoxProxy.executeCode(executionCodeRequest);

        // 4.4 根据执行结果修改执行状态，信息等
//        String message = executionCodeResponse.getMessage();
//        Integer statusOutPut = executionCodeResponse.getStatus();
        List<JudgeInfo> judgeInfoList = new ArrayList();
        executionCodeResponse.forEach(codeResponse -> {
            JudgeInfo judgeInfo = new JudgeInfo();
            JudgeInfo judgeInfoOutPut = codeResponse.getJudgeInfo();
            List<String> output = codeResponse.getOutput();

            // 根据沙箱的执行结果，设置题目的判题状态和信息
            // 设置初始状态为成功
            judgeInfo.setMessage(JudgeInfoMessageEnum.ACCEPTED.getValue());
            List<String> outPutOrigin = JSONUtil.toList(judgeCase, JudgeCase.class).stream().map(JudgeCase::getInput).collect(Collectors.toList());
            BeanUtils.copyProperties(judgeInfoOutPut,judgeInfo);
            if (output.size() != outPutOrigin.size()){
                judgeInfo.setMessage(JudgeInfoMessageEnum.WRONG_ANSWER.getValue());
            }
            for (int i = 0; i < outPutOrigin.size(); i++) {
                if (!output.get(i).equals(outPutOrigin.get(i))){
                    judgeInfo.setMessage(JudgeInfoMessageEnum.WRONG_ANSWER.getValue());
                }
            }
            // 假设java运行比其他语言运行多需要4秒
            if (judgeInfoOutPut.getTime() > judgeConfig.getTimeLimit() + JAVA_TIME) {
                judgeInfo.setMessage(JudgeInfoMessageEnum.TIME_LIMIT_EXCEEDED.getValue());
            }
            if (judgeInfoOutPut.getMemory() > judgeConfig.getMemoryLimit()) {
                judgeInfo.setMessage(JudgeInfoMessageEnum.MEMORY_LIMIT_EXCEEDED.getValue());
            }
            judgeInfoList.add(judgeInfo);
        });

        return judgeInfoList;
    }
}
